import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * LeetCode 146. LRU 缓存机制
 */
public class Solution_146 {

}

/**
 * 方法二：手写双向链表
 */
class LRUCache {
    /**
     * 双向链表节点类
     * <p>
     * 定义：靠近头部的键值对是最近使用的，而靠近尾部的键值对是最久未使用的
     */
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;

        public DLinkedNode() {
        }

        public DLinkedNode(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    // 通过缓存数据的键映射到其在双向链表中的位置
    private Map<Integer, DLinkedNode> cache = new HashMap<>();
    // 双向链表中节点数
    private int size;
    // 双向链表容量，即LRU的capacity
    private int capacity;
    // 虚拟头节点，虚拟尾节点
    private DLinkedNode dummyHead, dummyTail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        dummyHead = new DLinkedNode();
        dummyTail = new DLinkedNode();
        dummyHead.next = dummyTail;
        dummyTail.prev = dummyHead;
    }

    public int get(int key) {
        LRUCache.DLinkedNode node = cache.get(key);
        // 如果 key 不存在，则返回 -1
        if (node == null) {
            return -1;
        }
        // 如果 key 存在，由于最近访问，将其移到头部
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value) {
        LRUCache.DLinkedNode node = cache.get(key);
        if (node == null) {
            // 如果 key 不存在，使用 key 和 value 创建一个新的节点
            LRUCache.DLinkedNode newNode = new DLinkedNode(key, value);
            cache.put(key, newNode);
            // 在双向链表的头部添加该节点
            addToHead(newNode);
            size++;
            // 如果超出容量，则删除双向链表的尾部节点，并删除哈希表中对应的项
            if (size > capacity) {
                DLinkedNode tail = removeTail();
                cache.remove(tail.key);
                size--;
            }
        } else {
            // 如果 key 已存在，则更新其 value 值
            node.value = value;
            // 由于最近访问，还需将其移至双向链表头部
            moveToHead(node);
        }
    }

    /**
     * 在双向链表头部插入节点
     */
    private void addToHead(LRUCache.DLinkedNode node) {
        node.prev = dummyHead;
        node.next = dummyHead.next;
        dummyHead.next.prev = node;
        dummyHead.next = node;
    }

    /**
     * 移除双向链表尾节点
     */
    private LRUCache.DLinkedNode removeTail() {
        LRUCache.DLinkedNode returnNode = dummyTail.prev;
        removeNode(returnNode);
        return returnNode;
    }

    /**
     * 将双向链表中某节点移至头部
     */
    private void moveToHead(LRUCache.DLinkedNode node) {
        // 分两步进行：
        // 1. 将该节点删除
        removeNode(node);
        // 2. 在头部插入该节点
        addToHead(node);
    }

    /**
     * 移除双向链表中的某个节点
     */
    private void removeNode(LRUCache.DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
}

/**
 * 方法一：语言内置数据结构
 */
class LRUCache1 extends LinkedHashMap<Integer, Integer> {
    private int capacity;

    public LRUCache1(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    public int get(int key) {
        return super.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        super.put(key, value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity;
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * 
 * LRUCache obj = new LRUCache(capacity);
 * 
 * int param_1 = obj.get(key);
 * 
 * obj.put(key,value);
 */
